package roboGP;

/**
 * Rappresenta tutto il genoma di un Robot.
 */

public class RobotGenoma 
{
	public static double TREE_INIT_METHOD_SPLIT = 0.5;
	
	private Tree _main;
	private Tree _onScannedRobot;
	private Tree _onHitByBullet;
	private Tree _onHitWall;
	private Tree _onHitRobot;

	public RobotGenoma() {
		_main = null;
		_onHitRobot = null;
		_onScannedRobot = null;
		_onHitWall = null;
		_onHitByBullet= null;
	}
	
	public RobotGenoma(Tree main, Tree onScannedRobot, Tree onHitByBullet, 
			Tree onHitWall, Tree onHitRobot)
	{
		_main = main;
		_onScannedRobot = onScannedRobot;
		_onHitByBullet = onHitByBullet;
		_onHitWall = onHitWall;
		_onHitRobot = onHitRobot;
	}
	
	/**
	 * Inizializza tutti gli alberi che compongono il genoma. Il modo di inizializzazione viene scelto a caso.
	 * @param initMaxDepth La profondità iniziale di ogni albero.
	 * @param treeMaxDepth La profondità massima di ogni alberi.
	 */
	public RobotGenoma(DepthsVector initMaxDepth, DepthsVector treeMaxDepth)
	{
		Tree.InitType initType = Tree.InitType.GROW;
		if (Math.random() < TREE_INIT_METHOD_SPLIT)
			initType = Tree.InitType.FULL;

		_main = new Tree(EvolutionChamber.actionMain, EvolutionChamber.condMain, 
				initMaxDepth.getMain(), initType, treeMaxDepth.getMain());
		_onScannedRobot = new Tree(EvolutionChamber.actionOnScanned, EvolutionChamber.condOnScanned, 
				initMaxDepth.getOnScannedRobot(), initType, treeMaxDepth.getOnScannedRobot());
		_onHitByBullet = new Tree(EvolutionChamber.actionByBullet, EvolutionChamber.condByBullet, 
				initMaxDepth.getOnHitByBullet(), initType, treeMaxDepth.getOnHitByBullet());
		_onHitWall = new Tree(EvolutionChamber.actionOnHitWall, EvolutionChamber.condOnHitWall, 
				initMaxDepth.getOnHitWall(), initType, treeMaxDepth.getOnHitWall());
		_onHitRobot = new Tree(EvolutionChamber.actionOnHitRobot, EvolutionChamber.condOnHitRobot, 
				initMaxDepth.getOnHitRobot(), initType, treeMaxDepth.getOnHitRobot());
	}
	
	/**
	 * Esegue il crossover tra due genomi.
	 * @param genoma1 Genoma del primo genitore.
	 * @param genoma2 Genoma del secondo genitore.
	 */
	public static void crossoverGenoma(RobotGenoma genoma1, RobotGenoma genoma2)
	{
		Tree.crossover(genoma1.getMain(), genoma2.getMain());
		Tree.crossover(genoma1.getOnHitByBullet(), genoma2.getOnHitByBullet());
		Tree.crossover(genoma1.getOnHitRobot(), genoma2.getOnHitRobot());
		Tree.crossover(genoma1.getOnScannedRobot(), genoma2.getOnScannedRobot());
		Tree.crossover(genoma1.getOnHitWall(), genoma2.getOnHitWall());
	}
	
	/**
	 * Crea un nuovo genoma a partire da uno esistente mutando un nodo di ogni albero 
	 * in un nuovo nodo dello stesso tipo oppure in un sottoalbero.
	 * @param subtree Se true allora la mutazione sarà di tipo subtree, se false la mutazione avverà con un nodo dello stesso tipo
	 * @return rb Un nuovo genoma, mutazione del precedente.
	 */
	public RobotGenoma mutateGenoma(boolean subtree) {
		RobotGenoma rb;
		if(subtree){
			Tree main = _main.subTreeMutate(EvolutionChamber.actionMain, EvolutionChamber.condMain);
			Tree onScannedRobot = _onScannedRobot.subTreeMutate(EvolutionChamber.actionOnScanned, EvolutionChamber.condOnScanned);
			Tree onHitByBullet = _onHitByBullet.subTreeMutate(EvolutionChamber.actionByBullet, EvolutionChamber.condByBullet);
			Tree onHitRobot = _onHitRobot.subTreeMutate(EvolutionChamber.actionOnHitRobot, EvolutionChamber.condOnHitRobot);
			Tree onHitWall = _onHitWall.subTreeMutate(EvolutionChamber.actionOnHitWall, EvolutionChamber.condOnHitWall);
			rb = new RobotGenoma(main, onScannedRobot, onHitByBullet, onHitWall, onHitRobot);
		}else{
			Tree main = _main.mutate(EvolutionChamber.actionMain, EvolutionChamber.condMain);
			Tree onScannedRobot = _onScannedRobot.mutate(EvolutionChamber.actionOnScanned, EvolutionChamber.condOnScanned);
			Tree onHitByBullet = _onHitByBullet.mutate(EvolutionChamber.actionByBullet, EvolutionChamber.condByBullet);
			Tree onHitRobot = _onHitRobot.mutate(EvolutionChamber.actionOnHitRobot, EvolutionChamber.condOnHitRobot);
			Tree onHitWall = _onHitWall.mutate(EvolutionChamber.actionOnHitWall, EvolutionChamber.condOnHitWall);
			rb = new RobotGenoma(main, onScannedRobot, onHitByBullet, onHitWall, onHitRobot);
		}
		return rb;
	}
	
	public RobotGenoma copyGenoma()
	{
		RobotGenoma copy = new RobotGenoma(_main.copy(), _onScannedRobot.copy(), 
				_onHitByBullet.copy(), _onHitWall.copy(), _onHitRobot.copy());
		return copy;
	}

	public void setMain(Tree main)
	{
		_main = main;
	}
	
	public void setOnScannedRobot(Tree onScannedRobot)
	{
		_onScannedRobot = onScannedRobot;
	}
	
	public void setOnHitByBullet(Tree onHitByBullet)
	{
		_onHitByBullet = onHitByBullet;
	}
	
	public void setOnHitWall(Tree onHitWall)
	{
		_onHitWall = onHitWall;
	}
	
	public void setonHitRobot(Tree onHitRobot)
	{
		_onHitRobot = onHitRobot;
	}
	
	public Tree getMain()
	{
		return _main;
	}
	
	public Tree getOnScannedRobot()
	{
		return _onScannedRobot;
	}
	
	public Tree getOnHitByBullet()
	{
		return _onHitByBullet;
	}
	
	public Tree getOnHitWall()
	{
		return _onHitWall;
	}
	
	public Tree getOnHitRobot()
	{
		return _onHitRobot;
	}
	
	static class DepthsVector
	{
		private int _main;
		private int _onScannedRobot;
		private int _onHitByBullet;
		private int _onHitWall;
		private int _onHitRobot;
				
		public DepthsVector(int main, int onScannedRobot, int onHitByBullet, int onHitWall, int onHitRobot)
		{
			_main = main;
			_onScannedRobot = onScannedRobot;
			_onHitByBullet = onHitByBullet;
			_onHitWall = onHitWall;
			_onHitRobot = onHitRobot;
		}
		
		public int getMain()
		{
			return _main;
		}
		
		public int getOnScannedRobot()
		{
			return _onScannedRobot;
		}
		
		public int getOnHitByBullet()
		{
			return _onHitByBullet;
		}
		
		public int getOnHitWall()
		{
			return _onHitWall;
		}
		
		public int getOnHitRobot()
		{
			return _onHitRobot;
		}
	}
}
